#include <iostream>
#include <libinput.h>

#include "linux/input.h"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <getopt.h>
#include <inttypes.h>
#include <libudev.h>
#include <limits.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>

#include <libevdev/libevdev.h>

#include "backend_input.h"

static int open_restricted(const char *path, int flags, void *user_data)
{
    bool *grab = (bool *)user_data;
    int fd = open(path, flags);

    if (fd < 0)
        fprintf(stderr, "Failed to open %s (%s)\n", path, strerror(errno));
    else if (grab && *grab && ioctl(fd, EVIOCGRAB, (void *)1) == -1)
        fprintf(stderr, "Grab requested, but failed for %s (%s)\n", path, strerror(errno));

    return fd < 0 ? -errno : fd;
}

static void close_restricted(int fd, void *user_data)
{
    close(fd);
}

static const struct libinput_interface interface = {
    .open_restricted = open_restricted,
    .close_restricted = close_restricted,
};

static int handle_and_print_events(struct libinput *li)
{
    int rc = -1;
    struct libinput_event *ev;

    libinput_dispatch(li);
    while ((ev = libinput_get_event(li))) {
        // print_event_header(ev);

        switch (libinput_event_get_type(ev)) {
        case LIBINPUT_EVENT_NONE:
            abort();
        case LIBINPUT_EVENT_DEVICE_ADDED:
            // print_device_notify(ev);
            // tools_device_apply_config(libinput_event_get_device(ev),
            // 			  &options);
            break;
        case LIBINPUT_EVENT_DEVICE_REMOVED:
            // print_device_notify(ev);
            break;
        case LIBINPUT_EVENT_POINTER_BUTTON: {
            struct libinput_event_pointer *button_event = libinput_event_get_pointer_event(ev);
            printf("[libinput] Button %d %s\n",
                   libinput_event_pointer_get_button(button_event),
                   libinput_event_pointer_get_button_state(button_event) == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed" : "released");
            // print_pointer_button_event(ev);
        }

        break;
        default:
            break;
        }

        libinput_event_destroy(ev);
        libinput_dispatch(li);
        rc = 0;
    }
    return rc;
}

static uint32_t start_time;
static volatile sig_atomic_t stop = 0;

static void mainloop(struct libinput *li)
{
    struct pollfd fds;

    fds.fd = libinput_get_fd(li);
    fds.events = POLLIN;
    fds.revents = 0;

    /* Handle already-pending device added events */
    if (handle_and_print_events(li))
        fprintf(stderr,
                "Expected device added events on startup but got none. "
                "Maybe you don't have the right permissions?\n");

    /* time offset starts with our first received event */
    if (poll(&fds, 1, -1) > -1) {
        struct timespec tp;

        clock_gettime(CLOCK_MONOTONIC, &tp);
        start_time = tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
        do {
            handle_and_print_events(li);
        } while (!stop && poll(&fds, 1, -1) > -1);
    }
    printf("End of mainloop");

    printf("\n");
}

static void sighandler(int signal, siginfo_t *siginfo, void *userdata)
{
    printf("Signal received: %d", signal);
    stop = 1;
}

int backend_input()
{
    struct sigaction act;

    memset(&act, 0, sizeof(act));
    act.sa_sigaction = sighandler;
    act.sa_flags = SA_SIGINFO;

    // if (sigaction(SIGINT, &act, NULL) == -1) {
    // 	fprintf(stderr, "Failed to set up signal handling (%s)\n",
    // 			strerror(errno));
    // 	return EXIT_FAILURE;
    // }

    const char *seat = "seat0";
    struct libinput *context;
    struct udev *udev = udev_new();

    if (!udev) {
        fprintf(stderr, "Failed to initialize udev\n");
        return 1;
    }

    context = libinput_udev_create_context(&interface, nullptr, udev);
    if (!context) {
        fprintf(stderr, "Failed to initialize context from udev\n");
        return 1;
    }

    if (libinput_udev_assign_seat(context, seat)) {
        fprintf(stderr, "Failed to set seat\n");
        libinput_unref(context);
        context = NULL;
        return 1;
    }
    udev_unref(udev);

    mainloop(context);

    // Release resources
    libinput_unref(context);

    return 0;
}
